/*
 * scenemngr.cpp
 *
 *  Created on: 10-ott-2008
 *      Author: fpistonesi
 */
/*

 Copyright (c) 2008-2009 Fabrizio Pistonesi

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

 */

#include "scenemngr.h"

SceneMngr::SceneMngr(IrrlichtDevice* device)
{
  if(!device) printf("Device fail!");

  Device = device;
  Subtitle = new SubTitle(Device);
  CameraCtrl = new Camera(Device);
  CursorCtrl = new Cursor(Device);
  TransitionCtrl = new Transition(Device);

  NodeCount = 0;
  //OverlayStack=(Node **)malloc(16*sizeof(Node*));

  SelectedSensor = 0;

  AudioDev = OpenDevice();
  if (!AudioDev)
    {
      // failure
      printf("Sound device failure.\n");
    }
  else
    {
      printf("Audio device: %s\n", AudioDev->getName());
    }
}

SceneMngr::~SceneMngr()
{

}

bool SceneMngr::loadProject(stringc project)
{
  IFileSystem* fileSystem = Device->getFileSystem();
  ILogger* logger = Device->getLogger();

  fileSystem->changeWorkingDirectoryTo(project.c_str());
  stringc prjfile = "main.xml";
  IReadFile* file = fileSystem->createAndOpenFile("main.xml");
  if (!file)
    {
      logger->log("Unable to open project ", ELL_ERROR);
      return false;
    }
  else
    {
      Project = project.c_str();
      fileSystem->changeWorkingDirectoryTo(project.c_str());
      IFileList* fl = fileSystem->createFileList();
      // List all files in the main.xml folder, if is a directory add it to the list.
      // In this way node folders can contain mnemonics and is in alphabetical order.
      // Index "0" is reserved and is the start node.
      logger->log("The following nodes are detected:");
      for (int i = 1; i < (int) fl->getFileCount(); i++)
        {
          if (fl->isDirectory(i) && (fl->getFileName(i) != "..") && (fl->getFileName(i) != "..")) //! TODO: remove this warning
            {
              std::string filename;
              filename = fl->getFileName(i).c_str();
              if (filename.at(0) != '.')
                {
                NodeList.push_back(filename); //a.viarengo vector substitute C-style Array
                logger->log(fl->getFileName(i).c_str());
                }
            }
        }
      IXMLReader* reader = fileSystem->createXMLReader(file);
      if (!reader)
        {
          Device->getLogger()->log("Project has not a valid XML file",
              file->getFileName().c_str(), ELL_ERROR);
          return false;
        }
      while (reader->read())
        {
          bool endreached = false;
          const wchar_t* name = reader->getNodeName();
          switch (reader->getNodeType())
            {
          case EXN_ELEMENT_END:
            if (stringw(L"project") == name) endreached = true;
            return true;
            break;
            case EXN_ELEMENT:
            if (stringw(L"attributes") == name)
              {
                IAttributes* attr = fileSystem->createEmptyAttributes(Device->getVideoDriver());
                attr->read(reader);
                Title = attr->getAttributeAsStringW("title");
                StartNode = attr->getAttributeAsInt("startnode");
                Device->setWindowCaption(Title.c_str());
                loadNode(NodeList[StartNode],StartNode);
                switch(ThisNode->getType())
                {
                  case ENT_SLIDE:
                    getCamera()->setType(ECT_FIXED);
                    break;
                  case ENT_PANEL:
                    getCamera()->setType(ECT_FIXED);
                    break;
                  case ENT_CUBIC:
                    getCamera()->setType(ECT_FPS);
                    break;
                  case ENT_SPHERICAL:
                    getCamera()->setType(ECT_FPS);
                    break;
                }
              }
            break;
            default:
            break;
          }
        if(endreached) break;
      }
    reader->drop();
  }
return 0;
}


bool SceneMngr::saveNode(const c8 *filename)
{
  IWriteFile* file = Device->getFileSystem()->createAndWriteFile(filename);
  if(!file) return false;
  IXMLWriter* writer = Device->getFileSystem()->createXMLWriter(file);
  if(!writer) return false;

  writer->writeXMLHeader();

  // node properties
  io::IAttributes* attr = Device->getFileSystem()->createEmptyAttributes(Device->getVideoDriver());
  ThisNode->serialize(attr);

  if (attr->getAttributeCount() != 0)
    {
      attr->write(writer);
      writer->writeLineBreak();
    }

  // patches
  const wchar_t* patchObj = L"patch";
  writer->writeElement(patchObj);
  writer->writeLineBreak();
  for(unsigned int i = 0; i < ThisNode->Patches.size(); i++) //a.viarengo
    {
      attr->clear();
      ThisNode->getPatch(i+1)->serialize(attr);
      attr->write(writer);
    }
  writer->writeClosingTag(patchObj);
  writer->writeLineBreak();

  // sounds
  const wchar_t* soundObj = L"sound";
  writer->writeElement(soundObj);
  writer->writeLineBreak();
  for(unsigned int i = 0; i < ThisNode->Sounds.size(); i++)  //a.viarengo
    {
      attr->clear();
      ThisNode->getSound(i+1)->serialize(attr);
      attr->write(writer);
    }
  writer->writeClosingTag(soundObj);
  writer->writeLineBreak();

  //end
  writer->drop();
  return true;
}

Node* SceneMngr::loadNode(std::string sNodename, s32 id)
{
  const c8* nodename = sNodename.c_str();
  Device->getFileSystem()->changeWorkingDirectoryTo(nodename);

  stringc filename = "node.xml";

  IReadFile* file = Device->getFileSystem()->createAndOpenFile(filename.c_str());
  if(!file)
    {
      Device->getLogger()->log("Unable to open node file", filename.c_str(), ELL_ERROR);
      Device->getFileSystem()->changeWorkingDirectoryTo("..");
      return 0;
    }
  else
    {
      IXMLReader* reader = Device->getFileSystem()->createXMLReader(file);
      if (!reader)
        {
          Device->getLogger()->log("Scene is not a valid XML file", file->getFileName().c_str(), ELL_ERROR);
          Device->getFileSystem()->changeWorkingDirectoryTo("..");
          return 0;
        }

// Loading started ...
      while(reader->read())
        {
          bool endreached = false;
          const wchar_t* name = reader->getNodeName();
          switch(reader->getNodeType())
          {
            case EXN_ELEMENT_END:
              if (core::stringw(L"node") == name) endreached = true;
              //return 0;
              break;
            case EXN_ELEMENT:
              if (core::stringw(L"attributes") == name)
                {
                  IAttributes* attr = Device->getFileSystem()->createEmptyAttributes(Device->getVideoDriver());
                  attr->read(reader);

                  /*stringc source = attr->getAttributeAsString("bgmusic");
                  if(BgMusic->isPlaying()) BgMusic->stop();
                  BgMusic = new Sound(AudioDev,getCamera()->ICamera[0],source,true,true,1,0,1,0,0);*/

                  stringc type = attr->getAttributeAsString("type");
                  if(type == "Slide")
                    {
                      stringc backgnd = attr->getAttributeAsString("background");
                      ThisNode = new Slide(Device,id,backgnd.c_str());
                    }
                  if(type == "Panel")
                    {
                      stringc backgnd = attr->getAttributeAsString("background");
                      position2d<s32> position = attr->getAttributeAsPosition2d("position");
                      SColor color = attr->getAttributeAsColor("color");
                      ThisNode = new Panel(Device,id,backgnd,position,color);
                    }
                  if(type == "Cubic")
                    {
                      stringc side[6];
                      side[0] = attr->getAttributeAsString("front");
                      side[1] = attr->getAttributeAsString("right");
                      side[2] = attr->getAttributeAsString("back");
                      side[3] = attr->getAttributeAsString("left");
                      side[4] = attr->getAttributeAsString("up");
                      side[5] = attr->getAttributeAsString("down");
                      ThisNode = new Cubic(Device,id,side);
                    }
                  if(type =="Spherical")
                    {
                      stringc equirect = attr->getAttributeAsString("equirect");
                      ThisNode = new Spherical(Device,id,equirect);
                    }
                  ThisNode->AudioDev = AudioDev;
                  ThisNode->setId(id);
                  int cursor = attr->getAttributeAsInt("cursor");
                  ThisNode->DefaultCursor = cursor;
                  getCursor()->setCursor(cursor);
                  NodeCount++;
                }
              break;
            default:
              break;
          }

          if(stringw(L"sound") == name)loadSounds(reader,ThisNode);
          if(stringw(L"patch") == name)loadPatches(reader,ThisNode);
          if(stringw(L"sensor") == name)loadSensors(reader,ThisNode);

          if(endreached) break;
        }
      reader->drop();
    }
  //
  switch(ThisNode->getType())
  {
    case ENT_SLIDE:
      getCamera()->setType(ECT_FIXED);
      getCamera()->ICamera[1]->setInputReceiverEnabled(false);
      getCamera()->ICamera[1]->setRotation(vector3df(0,0,0));
      break;
    case ENT_PANEL:
      getCamera()->setType(ECT_FIXED);
      getCamera()->ICamera[1]->setInputReceiverEnabled(false);
      getCamera()->ICamera[1]->setInputReceiverEnabled(false);
      break;
    case ENT_CUBIC:
      getCamera()->setType(ECT_FPS);
      getCamera()->ICamera[1]->setInputReceiverEnabled(true);
      break;
    case ENT_SPHERICAL:
      getCamera()->setType(ECT_FPS);
      getCamera()->ICamera[1]->setInputReceiverEnabled(true);
      break;
  }
  Device->getFileSystem()->changeWorkingDirectoryTo("..");
  if(OverlayStack.size() != 0) OverlayStack.erase(OverlayStack.begin());
  OverlayStack.insert(OverlayStack.begin(),ThisNode);
  return ThisNode;
}

void SceneMngr::loadPatches(IXMLReader *reader, Node *node)
{
  while (reader->read())
    {
      const wchar_t* name = reader->getNodeName();

      switch (reader->getNodeType())
        {
      case EXN_ELEMENT_END:
        if (core::stringw(L"patch")==name)
        return;
        break;
      case EXN_ELEMENT:
        if (core::stringw(L"attributes")==name)
          {
            // read attribute list
            io::IAttributes* attr = Device->getFileSystem()->createEmptyAttributes(Device->getVideoDriver());
            attr->read(reader);

            stringc image = attr->getAttributeAsString("image");
            f32 x = attr->getAttributeAsFloat("x");
            f32 y = attr->getAttributeAsFloat("y");
            f32 w = attr->getAttributeAsFloat("w");
            f32 h = attr->getAttributeAsFloat("h");
            f32 az = attr->getAttributeAsFloat("az");
            f32 el = attr->getAttributeAsFloat("el");
            int face = (int)attr->getAttributeAsFloat("face");
            SColor color = attr->getAttributeAsColor("color");
            vector3df rotation = attr->getAttributeAsVector3d("rotation");
            bool visible = attr->getAttributeAsBool("visible");

            switch(node->getType())
            {
              case ENT_SLIDE:
                node->addPatch(image,visible,x,y,w,h,1,color);
                node->Patches.back()->rotate(rotation); //a.viarengo  using vector
                break;
              case ENT_PANEL:
                node->addPatch(image,(s32)x,(s32)y,(s32)w,(s32)h,color,visible);
                break;
              case ENT_CUBIC:
                node->addPatch(image,visible,x,y,w,h,face,color);
                node->Patches.back()->rotate(rotation); //a.viarengo  using vector
                break;
              case ENT_SPHERICAL:
                node->addPatch(image,visible,az,el,w,h,color);
                node->Patches.back()->rotate(rotation); //a.viarengo  using vector
                break;
            }
           }
        break;
      default:
        break;
        }
    }
}

void SceneMngr::loadSounds(IXMLReader *reader, Node* node)
{
  while(reader->read())
    {
      const wchar_t* name = reader->getNodeName();
      switch (reader->getNodeType())
      {
        case EXN_ELEMENT_END:
          if(stringw(L"sound") == name)
            return;
          break;
        case EXN_ELEMENT:
          if(stringw(L"attributes") == name)
            {
              io::IAttributes* attr = Device->getFileSystem()->createEmptyAttributes(Device->getVideoDriver());
              attr->read(reader);
              stringc source = attr->getAttributeAsString("source");
              f32 azimuth = attr->getAttributeAsFloat("azimuth");
              f32 elevation = attr->getAttributeAsFloat("elevation");
              float volume = attr->getAttributeAsFloat("volume");
              float pan = attr->getAttributeAsFloat("pan");
              float pitch = attr->getAttributeAsFloat("pitch");
              bool repeat = attr->getAttributeAsBool("repeat");
              bool autostart = attr->getAttributeAsBool("autostart");
              node->addSound(source,autostart,repeat,volume,pan,pitch,azimuth,elevation);

              node->Sounds.back()->SoundRotation = Device->getSceneManager()->addEmptySceneNode();
              node->Sounds.back()->SoundRotation->setRotation(vector3df(elevation,azimuth,0));
              node->Sounds.back()->SoundRotation->setIsDebugObject(true);
              node->Sounds.back()->SoundCube = Device->getSceneManager()->addBillboardSceneNode(node->Sounds.back()->SoundRotation);
              node->Sounds.back()->SoundCube->setPosition(vector3df(0,0,50.0f));
              node->Sounds.back()->SoundCube->setMaterialTexture(0,Device->getVideoDriver()->getTexture("sound.png"));
              node->Sounds.back()->SoundCube->setMaterialFlag(EMF_LIGHTING,false);
              node->Sounds.back()->SoundCube->setMaterialType(EMT_TRANSPARENT_ALPHA_CHANNEL);
              node->Sounds.back()->SoundCube->setMaterialFlag(EMF_ANISOTROPIC_FILTER,true);
              ThisNode->Root->addChild(node->Sounds.back()->SoundRotation);
              node->Sounds.back()->SoundCube->setVisible(false);
            }
        default:
          break;
      }
    }
}

void SceneMngr::loadSensors(io::IXMLReader *reader, Node *node)
{
  while(reader->read())
    {
      const wchar_t* name = reader->getNodeName();
      switch (reader->getNodeType())
      {
        case EXN_ELEMENT_END:
          if(stringw(L"sensor") == name)
            return;
          break;
        case EXN_ELEMENT:
          if(stringw(L"attributes") == name)
            {
              io::IAttributes* attr = Device->getFileSystem()->createEmptyAttributes(Device->getVideoDriver());
              attr->read(reader);
              float width = attr->getAttributeAsFloat("w");
              float height = attr->getAttributeAsFloat("h");
              int x = attr->getAttributeAsInt("x");
              int y = attr->getAttributeAsInt("y");
              float az = attr->getAttributeAsFloat("az");
              float el = attr->getAttributeAsFloat("el");
              int target = attr->getAttributeAsInt("target");
              int effect = attr->getAttributeAsInt("effect");
              float factor = attr->getAttributeAsFloat("factor");
              int duration = attr->getAttributeAsInt("duration");
              int cursor = attr->getAttributeAsInt("cursor");
              bool enable = attr->getAttributeAsBool("enable");
              std::string message = attr->getAttributeAsString("message").c_str();
              if(node->getType() != ENT_PANEL)
                {
                  node->addSensor(width,height,az,el,target,effect,factor,duration,cursor,message,enable);
                }
              if(node->getType() == ENT_PANEL)
                {
                  node->addSensor((s32)width,(s32)height,x,y,target,effect,factor,duration,cursor,message,enable);
                }

            }
        default:
          break;
      }
    }
  SelectedSensor = 0;
}

bool SceneMngr::gotoNode(s32 id, ETRANSITION_TYPE effect,int time, float factor)
{
  getCursor()->ScreenCursor->setVisible(false);
  TransitionCtrl->start(effect,time,factor);

  ThisNode->~Node();
  //NodeCount--;
  loadNode(NodeList.at(id),id);

  getCursor()->ScreenCursor->setVisible(true);

  return false;
}


SubTitle* SceneMngr::getSubtitle()
{
  return Subtitle;
}

Camera* SceneMngr::getCamera()
{
  return CameraCtrl;
}

Cursor* SceneMngr::getCursor()
{
  return CursorCtrl;
}

Transition *SceneMngr::getTransition()
{
  return TransitionCtrl;
}

void SceneMngr::update(const SEvent & event)
{
  getCursor()->OnEvent(event);
  updateListenerPosition(event);
  updateSensors(event);
}

void SceneMngr::updateListenerPosition(const SEvent& event)
{
  if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
    {
      for (unsigned int i = 0; i < ThisNode->Sounds.size(); i++)
        {
          if (ThisNode->getSound(i) && ThisNode->getSound(i)->Stream
              && ((ThisNode->getType() == ENT_CUBIC) || (ThisNode->getType()
                  == ENT_SPHERICAL)))
            {
              ThisNode->getSound(i)->update();
            }
        }
    }
}

void SceneMngr::updateSensors(const SEvent& event)
{
  if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
    {
      for(unsigned int i = 0; i < OverlayStack.size(); i++)
        {
          for(unsigned int j = 0; j < OverlayStack.at(i)->Sensors.size(); j++)
            {
              Sensor *s = OverlayStack.at(i)->Sensors.at(j);
              s->check();
              if(s->Selected && s != SelectedSensor && s->Enable)
                {
                  SelectedSensor = s;
                  getCursor()->setCursor(s->DefAction.Cursor);
                }
              if(!s->Selected && s->Enable && SelectedSensor != 0)
                {
                  SelectedSensor = 0;
                  getCursor()->setCursor(OverlayStack.at(i)->DefaultCursor);
                }

            }
        }

    }

  if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
    {
      //getSubtitle()->print(L"CLICK");
      if(SelectedSensor && SelectedSensor->Enable)
        {
          //getSubtitle()->print(L"Active Sensor clicked");
          Action a = SelectedSensor->DefAction;
          if(a.Message != "")
            {
              stringw s = a.Message.c_str();
              getSubtitle()->print(s.c_str());
            }
          if(a.Target > 0)
            {
              switch (a.Effect)
                {
                  case 0:
                    gotoNode(a.Target, ETT_FADE, a.Duration, a.Factor);
                    break;
                  case 1:
                    gotoNode(a.Target, ETT_ROTATE, a.Duration, a.Factor);
                    break;
                  case 2:
                    gotoNode(a.Target, ETT_WIPE, a.Duration, a.Factor);
                    break;
                  default:
                    break;
                }
              SelectedSensor->Selected = false;
              SelectedSensor = 0;
            }
        }
    }
}






